﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;


namespace Tzync
{
    public class Sync
    {
        private List<Difference> differences;
        private List<TreeNode> arrNodes;
        private Stack<Difference> folderStack;
        private SystemType systemName;
        private String storeFilesDir, rootAddress;
        bool secondTime;

        
        /// <summary>
        /// Constructor called for first time and second syncing
        /// </summary>
        /// <param name="arrNodes">contains the metadata</param>
        /// <param name="secondTime">check secondTime is true</param>
        /// <param name="st">host or guest</param>
        /// <param name="profilePath"></param>
        public Sync(List<TreeNode> arrNodes, bool secondTime, SystemType st, String profilePath)
        {
            this.arrNodes = arrNodes;
            this.secondTime = secondTime;
            systemName = st;

            storeFilesDir = profilePath;
            storeFilesDir = Path.Combine(storeFilesDir, "Files");
            //Creating the directory
            Directory.CreateDirectory(storeFilesDir);
        }

        /// <summary>
        /// Called for normal syncing
        /// </summary>
        /// <param name="differences">Files folders to change</param>
        /// <param name="arrNodes">Metadata</param>
        /// <param name="systemName">host or guest</param>
        /// <param name="profilePath">the profile's path</param>
        public Sync(List<Difference> differences, List<TreeNode> arrNodes, SystemType systemName, String profilePath)
        {
            this.differences = differences;
            this.arrNodes = arrNodes;
            this.systemName = systemName;
            folderStack = new Stack<Difference>();

            storeFilesDir = profilePath;
            storeFilesDir = Path.Combine(storeFilesDir, "Files");
            //Creating the directory
            Directory.CreateDirectory(storeFilesDir);
        }

        /// <summary>
        /// Calls either file or folder reconcile method
        /// </summary>
        public void Reconcile()
        {
            long fileSize = ComputeFileSizeToTransfer();
            if (fileSize != 0)
            {
                bool hardDiskSpaceEnough = CheckDiskSpace(fileSize);

                if (!hardDiskSpaceEnough) throw new DiskSpaceNotEnoughException();
            }
            
            foreach (Difference d in differences)
            {
                if (d.GetItemType() == Type.File)
                {
                    ReconcileFile(d);
                }
                else if (d.GetItemType() == Type.Folder)
                {
                    ReconcileFolder(d);
                }
            }
            UpdateFoldersTimeandSize();
        }

        /// <summary>
        /// Does syncing based on the dfferent sync operations for file
        /// </summary>
        /// <param name="d"></param>
        private void ReconcileFile(Difference d)
        {
            TreeNode tNode;
            DateTimeParser dtp;
            String customFilePath = "";
            FileInfo fileInfo;

            tNode = d.GetPartnerFile();
            dtp = new DateTimeParser(d.GetSysFile().LastWriteTime);

            #region Creation
            if (d.GetPairType().Second == OperationType.Creation)
            {
                //ReconcileCreation(d.GetPairType().First, 
                if (d.GetPairType().First == DirectionType.SystemToHD)
                {
                    if (systemName == SystemType.Host)
                    {
                        //copyFromSystemToHD(storeFilesDir, tNode.getFullPathA());
                        customFilePath = Path.Combine(storeFilesDir, StripString(tNode.getFullPathA()));
                        if (File.Exists(customFilePath)) File.Delete(customFilePath); // check if earlier file exits. if then juz delete it
                        File.Copy(tNode.getFullPathA(), customFilePath);

                    }
                    else
                    {
                        //copyFromSystemToHD(storeFilesDir, tNode.getFullPathB());
                        customFilePath = Path.Combine(storeFilesDir, StripString(tNode.getFullPathB()));
                        if (File.Exists(customFilePath)) File.Delete(customFilePath); // check if earlier file exits. if then juz delete it
                        File.Copy(tNode.getFullPathB(), customFilePath);
                    }
                }
                else if (d.GetPairType().First == DirectionType.HDToSystem)
                {
                    if (systemName == SystemType.Host)
                    {
                        //copyFromHDToSystem(storeFilesDir, tNode, type);
                        customFilePath = Path.Combine(storeFilesDir, StripString(tNode.getFullPathB()));
                        Console.WriteLine("Malakian: " + customFilePath);
                        Console.WriteLine("Malakian: " + Path.Combine(storeFilesDir, customFilePath));
                        File.Copy(Path.Combine(storeFilesDir, customFilePath), tNode.getFullPathA());
                        arrNodes[tNode.getId()].getVersionA().First = tNode.getVersionB().First;
                        arrNodes[tNode.getId()].getVersionA().Second = tNode.getVersionB().Second;
                        fileInfo = new FileInfo(tNode.getFullPathA());
                        dtp = new DateTimeParser(fileInfo.LastWriteTime);
                        arrNodes[tNode.getId()].setSyncA(dtp.GetNewDateTime());
                    }
                    else
                    {
                        //copyFromHDToSystem(storeFilesDir, tNode, type);
                        customFilePath = Path.Combine(storeFilesDir, StripString(tNode.getFullPathA()));
                        File.Copy(Path.Combine(storeFilesDir, customFilePath), tNode.getFullPathB());
                        arrNodes[tNode.getId()].getVersionB().First = tNode.getVersionA().First;
                        arrNodes[tNode.getId()].getVersionB().Second = tNode.getVersionA().Second;
                        fileInfo = new FileInfo(tNode.getFullPathB());
                        dtp = new DateTimeParser(fileInfo.LastWriteTime);
                        arrNodes[tNode.getId()].setSyncB(dtp.GetNewDateTime());
                    }
                    //Delete file in HD
                    try { File.Delete(customFilePath); }
                    catch (UnauthorizedAccessException e) { Console.WriteLine(e.Message); }
                }
            }
            #endregion

            #region Deletion
            else if (d.GetPairType().Second == OperationType.Deletion)
            {
                if (d.GetPairType().First == DirectionType.SystemToHD)
                {
                    if (systemName == SystemType.Host)
                    {
                        arrNodes[tNode.getId()].getVersionA().First = -1;
                        arrNodes[tNode.getId()].getVersionA().Second = 0;
                        dtp = new DateTimeParser(DateTime.Now);
                        arrNodes[tNode.getId()].setSyncA(dtp.GetNewDateTime());

                    }
                    else
                    {

                        arrNodes[tNode.getId()].getVersionB().First = 0;
                        arrNodes[tNode.getId()].getVersionB().Second = -1;
                        dtp = new DateTimeParser(DateTime.Now);
                        arrNodes[tNode.getId()].setSyncB(dtp.GetNewDateTime());
                    }
                }
                else if (d.GetPairType().First == DirectionType.HDToSystem)
                {
                    if (systemName == SystemType.Host)
                    {
                        File.Delete(tNode.getFullPathA());
                        arrNodes[tNode.getId()].getVersionA().First = tNode.getVersionB().First;
                        arrNodes[tNode.getId()].getVersionA().Second = tNode.getVersionB().Second;
                        dtp = new DateTimeParser(DateTime.Now);
                        arrNodes[tNode.getId()].setSyncA(dtp.GetNewDateTime());
                    }
                    else
                    {
                        File.Delete(tNode.getFullPathB());
                        arrNodes[tNode.getId()].getVersionB().First = tNode.getVersionA().First;
                        arrNodes[tNode.getId()].getVersionB().Second = tNode.getVersionA().Second;
                        dtp = new DateTimeParser(DateTime.Now);
                        arrNodes[tNode.getId()].setSyncB(dtp.GetNewDateTime());
                    }
                }
            }
            #endregion

            #region Modification
            else if (d.GetPairType().Second == OperationType.Modification)
            {
                if (d.GetPairType().First == DirectionType.SystemToHD)
                {
                    if (systemName == SystemType.Host)
                    {
                        customFilePath = Path.Combine(storeFilesDir, StripString(tNode.getFullPathA()));
                        if (File.Exists(customFilePath)) File.Delete(customFilePath); // check if earlier file exits. if then juz delete it
                        File.Copy(tNode.getFullPathA(), customFilePath);
                        arrNodes[tNode.getId()].getVersionA().First++;
                        fileInfo = new FileInfo(tNode.getFullPathA());
                        dtp = new DateTimeParser(fileInfo.LastWriteTime);
                        arrNodes[tNode.getId()].setSyncA(dtp.GetNewDateTime());
                    }
                    else
                    {
                        customFilePath = Path.Combine(storeFilesDir, StripString(tNode.getFullPathB()));
                        if (File.Exists(customFilePath)) File.Delete(customFilePath); // check if earlier file exits. if then juz delete it
                        File.Copy(tNode.getFullPathB(), customFilePath);
                        arrNodes[tNode.getId()].getVersionB().Second++;
                        fileInfo = new FileInfo(tNode.getFullPathB());
                        dtp = new DateTimeParser(fileInfo.LastWriteTime);
                        arrNodes[tNode.getId()].setSyncB(dtp.GetNewDateTime());
                    }
                }
                else if (d.GetPairType().First == DirectionType.HDToSystem)
                {
                    if (systemName == SystemType.Host)
                    {
                        if (File.Exists(tNode.getFullPathA()))
                            File.Delete(tNode.getFullPathA());
                        customFilePath = Path.Combine(storeFilesDir, StripString(tNode.getFullPathB()));
                        File.Copy(Path.Combine(storeFilesDir, customFilePath), tNode.getFullPathA());
                        arrNodes[tNode.getId()].getVersionA().First = tNode.getVersionB().First;
                        arrNodes[tNode.getId()].getVersionA().Second = tNode.getVersionB().Second;
                        fileInfo = new FileInfo(tNode.getFullPathA());
                        dtp = new DateTimeParser(fileInfo.LastWriteTime);
                        arrNodes[tNode.getId()].setSyncA(dtp.GetNewDateTime());
                    }
                    else
                    {
                        if (File.Exists(tNode.getFullPathB()))
                            File.Delete(tNode.getFullPathB());
                        customFilePath = Path.Combine(storeFilesDir, StripString(tNode.getFullPathA()));
                        File.Copy(Path.Combine(storeFilesDir, customFilePath), tNode.getFullPathB());
                        arrNodes[tNode.getId()].getVersionB().First = tNode.getVersionA().First;
                        arrNodes[tNode.getId()].getVersionB().Second = tNode.getVersionA().Second;
                        fileInfo = new FileInfo(tNode.getFullPathB());
                        dtp = new DateTimeParser(fileInfo.LastWriteTime);
                        arrNodes[tNode.getId()].setSyncB(dtp.GetNewDateTime());
                    }
                    //Delete file in HD
                    try { File.Delete(customFilePath); }
                    catch (UnauthorizedAccessException e) { Console.WriteLine(e.Message); }
                }
            }
            #endregion

            #region Modification Modification Conflict
            else if (d.GetPairType().Second == OperationType.ModModConflict)
            {
                if (d.GetPairType().First == DirectionType.SystemToHD)
                {
                    if (systemName == SystemType.Host)
                    {
                        // delete B's file in HD since user want to propagate system file to HD
                        customFilePath = Path.Combine(storeFilesDir, StripString(tNode.getFullPathB()));
                        File.Delete(customFilePath);
                        // copy system file to HD now
                        customFilePath = Path.Combine(storeFilesDir, StripString(tNode.getFullPathA()));
                        File.Copy(tNode.getFullPathA(), customFilePath);
                        arrNodes[tNode.getId()].getVersionA().First++;
                        fileInfo = new FileInfo(tNode.getFullPathA());
                        dtp = new DateTimeParser(fileInfo.LastWriteTime);
                        arrNodes[tNode.getId()].setSyncA(dtp.GetNewDateTime());
                    }
                    else
                    {
                        // delete A's file in HD since user want to propagate system file to HD
                        customFilePath = Path.Combine(storeFilesDir, StripString(tNode.getFullPathA()));
                        File.Delete(customFilePath);
                        // copy system file to HD now
                        customFilePath = Path.Combine(storeFilesDir, StripString(tNode.getFullPathB()));
                        File.Copy(tNode.getFullPathB(), customFilePath);
                        arrNodes[tNode.getId()].getVersionB().Second++;
                        fileInfo = new FileInfo(tNode.getFullPathB());
                        dtp = new DateTimeParser(fileInfo.LastWriteTime);
                        arrNodes[tNode.getId()].setSyncB(dtp.GetNewDateTime());
                    }
                }
                else if (d.GetPairType().First == DirectionType.HDToSystem)
                {
                    if (systemName == SystemType.Host)
                    {
                        File.Delete(tNode.getFullPathA());
                        customFilePath = Path.Combine(storeFilesDir, StripString(tNode.getFullPathB()));
                        File.Copy(Path.Combine(storeFilesDir, customFilePath), tNode.getFullPathA());
                        arrNodes[tNode.getId()].getVersionA().First = tNode.getVersionB().First;
                        arrNodes[tNode.getId()].getVersionA().Second = tNode.getVersionB().Second;
                        fileInfo = new FileInfo(tNode.getFullPathA());
                        dtp = new DateTimeParser(fileInfo.LastWriteTime);
                        arrNodes[tNode.getId()].setSyncA(dtp.GetNewDateTime());
                    }
                    else
                    {
                        File.Delete(tNode.getFullPathB());
                        customFilePath = Path.Combine(storeFilesDir, StripString(tNode.getFullPathA()));
                        File.Copy(Path.Combine(storeFilesDir, customFilePath), tNode.getFullPathB());
                        arrNodes[tNode.getId()].getVersionB().First = tNode.getVersionA().First;
                        arrNodes[tNode.getId()].getVersionB().Second = tNode.getVersionA().Second;
                        fileInfo = new FileInfo(tNode.getFullPathB());
                        dtp = new DateTimeParser(fileInfo.LastWriteTime);
                        arrNodes[tNode.getId()].setSyncB(dtp.GetNewDateTime());
                    }
                    //Delete file in HD
                    try { File.Delete(customFilePath); }
                    catch (UnauthorizedAccessException e) { Console.WriteLine(e.Message); }
                }
            }
            #endregion

            #region Modification Delettion Conflict
            else if (d.GetPairType().Second == OperationType.ModDeleteConflict)
            {
                if (d.GetPairType().First == DirectionType.SystemToHD)
                {
                    if (systemName == SystemType.Host)
                    {
                        if (arrNodes[tNode.getId()].getVersionB().First == 0 && arrNodes[tNode.getId()].getVersionB().Second == -1)
                        {
                            customFilePath = Path.Combine(storeFilesDir, StripString(tNode.getFullPathA()));
                            if (File.Exists(customFilePath)) File.Delete(customFilePath); // check if earlier file exits. if then juz delete it
                            File.Copy(tNode.getFullPathA(), customFilePath);
                            arrNodes[tNode.getId()].getVersionA().First++;
                            fileInfo = new FileInfo(tNode.getFullPathA());
                            dtp = new DateTimeParser(fileInfo.LastWriteTime);
                            arrNodes[tNode.getId()].setSyncA(dtp.GetNewDateTime());
                        }
                        else
                        {
                            arrNodes[tNode.getId()].getVersionA().First = -1;
                            arrNodes[tNode.getId()].getVersionA().Second = 0;
                            dtp = new DateTimeParser(DateTime.Now);
                            arrNodes[tNode.getId()].setSyncA(dtp.GetNewDateTime());
                        }

                    }
                    else
                    {
                        if (arrNodes[tNode.getId()].getVersionA().First == -1 && arrNodes[tNode.getId()].getVersionA().Second == 0)
                        {
                            customFilePath = Path.Combine(storeFilesDir, StripString(tNode.getFullPathB()));
                            if (File.Exists(customFilePath)) File.Delete(customFilePath); // check if earlier file exits. if then juz delete it
                            File.Copy(tNode.getFullPathB(), customFilePath);
                            arrNodes[tNode.getId()].getVersionB().Second++;
                            fileInfo = new FileInfo(tNode.getFullPathB());
                            dtp = new DateTimeParser(fileInfo.LastWriteTime);
                            arrNodes[tNode.getId()].setSyncB(dtp.GetNewDateTime());
                        }
                        else
                        {
                            arrNodes[tNode.getId()].getVersionB().First = 0;
                            arrNodes[tNode.getId()].getVersionB().Second = -1;
                            dtp = new DateTimeParser(DateTime.Now);
                            arrNodes[tNode.getId()].setSyncB(dtp.GetNewDateTime());
                        }
                    }
                }
                else if (d.GetPairType().First == DirectionType.HDToSystem)
                {
                    if (systemName == SystemType.Host)
                    {
                        if (arrNodes[tNode.getId()].getVersionB().First == 0 && arrNodes[tNode.getId()].getVersionB().Second == -1)
                        {
                            File.Delete(tNode.getFullPathA());
                            arrNodes[tNode.getId()].getVersionA().First = tNode.getVersionB().First;
                            arrNodes[tNode.getId()].getVersionA().Second = tNode.getVersionB().Second;
                            dtp = new DateTimeParser(DateTime.Now);
                            arrNodes[tNode.getId()].setSyncA(dtp.GetNewDateTime());
                        }
                        else
                        {
                            customFilePath = Path.Combine(storeFilesDir, StripString(tNode.getFullPathB()));
                            File.Copy(Path.Combine(storeFilesDir, customFilePath), tNode.getFullPathA());
                            arrNodes[tNode.getId()].getVersionA().First = tNode.getVersionB().First;
                            arrNodes[tNode.getId()].getVersionA().Second = tNode.getVersionB().Second;
                            fileInfo = new FileInfo(tNode.getFullPathA());
                            dtp = new DateTimeParser(fileInfo.LastWriteTime);
                            arrNodes[tNode.getId()].setSyncA(dtp.GetNewDateTime());
                            //Delete file in HD
                            try { File.Delete(customFilePath); }
                            catch (UnauthorizedAccessException e) { Console.WriteLine(e.Message); }
                        }

                    }
                    else
                    {
                        if (arrNodes[tNode.getId()].getVersionA().First == -1 && arrNodes[tNode.getId()].getVersionA().Second == 0)
                        {
                            File.Delete(tNode.getFullPathB());
                            arrNodes[tNode.getId()].getVersionB().First = tNode.getVersionA().First;
                            arrNodes[tNode.getId()].getVersionB().Second = tNode.getVersionA().Second;
                            dtp = new DateTimeParser(DateTime.Now);
                            arrNodes[tNode.getId()].setSyncB(dtp.GetNewDateTime());
                        }
                        else
                        {
                            customFilePath = Path.Combine(storeFilesDir, StripString(tNode.getFullPathA()));
                            File.Copy(Path.Combine(storeFilesDir, customFilePath), tNode.getFullPathB());
                            arrNodes[tNode.getId()].getVersionB().First = tNode.getVersionA().First;
                            arrNodes[tNode.getId()].getVersionB().Second = tNode.getVersionA().Second;
                            fileInfo = new FileInfo(tNode.getFullPathB());
                            dtp = new DateTimeParser(fileInfo.LastWriteTime);
                            arrNodes[tNode.getId()].setSyncB(dtp.GetNewDateTime());
                            //Delete file in HD
                            try { File.Delete(customFilePath); }
                            catch (UnauthorizedAccessException e) { Console.WriteLine(e.Message); }
                        }
                    }
                }
            }
            #endregion

            #region Creation Creation Conflict
            else if (d.GetPairType().Second == OperationType.CreationCreationConflict)
            {
                if (d.GetPairType().First == DirectionType.SystemToHD)
                {
                    if (systemName == SystemType.Host)
                    {
                        customFilePath = Path.Combine(storeFilesDir, StripString(tNode.getFullPathA()));
                        if (File.Exists(customFilePath)) File.Delete(customFilePath); // check if earlier file exits. if then juz delete it
                        File.Copy(tNode.getFullPathA(), customFilePath);
                        arrNodes[tNode.getId()].getVersionA().First++;
                        fileInfo = new FileInfo(tNode.getFullPathA());
                        dtp = new DateTimeParser(fileInfo.LastWriteTime);
                        arrNodes[tNode.getId()].setSyncA(dtp.GetNewDateTime());
                    }
                    else
                    {
                        customFilePath = Path.Combine(storeFilesDir, StripString(tNode.getFullPathB()));
                        if (File.Exists(customFilePath)) File.Delete(customFilePath); // check if earlier file exits. if then juz delete it
                        File.Copy(tNode.getFullPathB(), customFilePath);
                        arrNodes[tNode.getId()].getVersionB().Second++;
                        fileInfo = new FileInfo(tNode.getFullPathB());
                        dtp = new DateTimeParser(fileInfo.LastWriteTime);
                        arrNodes[tNode.getId()].setSyncB(dtp.GetNewDateTime());
                    }
                }
                else if (d.GetPairType().First == DirectionType.HDToSystem)
                {
                    if (systemName == SystemType.Host)
                    {
                        File.Delete(tNode.getFullPathA());
                        customFilePath = Path.Combine(storeFilesDir, StripString(tNode.getFullPathB()));
                        File.Copy(Path.Combine(storeFilesDir, customFilePath), tNode.getFullPathA());
                        arrNodes[tNode.getId()].getVersionA().First = tNode.getVersionB().First;
                        arrNodes[tNode.getId()].getVersionA().Second = tNode.getVersionB().Second;
                        fileInfo = new FileInfo(tNode.getFullPathA());
                        dtp = new DateTimeParser(fileInfo.LastWriteTime);
                        arrNodes[tNode.getId()].setSyncA(dtp.GetNewDateTime());
                    }
                    else
                    {
                        File.Delete(tNode.getFullPathB());
                        customFilePath = Path.Combine(storeFilesDir, StripString(tNode.getFullPathA()));
                        File.Copy(Path.Combine(storeFilesDir, customFilePath), tNode.getFullPathB());
                        arrNodes[tNode.getId()].getVersionB().First = tNode.getVersionA().First;
                        arrNodes[tNode.getId()].getVersionB().Second = tNode.getVersionA().Second;
                        fileInfo = new FileInfo(tNode.getFullPathB());
                        dtp = new DateTimeParser(fileInfo.LastWriteTime);
                        arrNodes[tNode.getId()].setSyncB(dtp.GetNewDateTime());
                    }
                    //Delete file in HD
                    try { File.Delete(customFilePath); }
                    catch (UnauthorizedAccessException e) { Console.WriteLine(e.Message); }
                }
            }
            #endregion

            #region Deletion Deletion Conflict
            else if (d.GetPairType().Second == OperationType.DeletionDeletionConflict)
            {
                if (d.GetPairType().First == DirectionType.SystemToHD)
                {
                    if (systemName == SystemType.Host)
                    {
                        arrNodes[tNode.getId()].getVersionA().First = -1;
                        arrNodes[tNode.getId()].getVersionA().Second = 0;
                        dtp = new DateTimeParser(DateTime.Now);
                        arrNodes[tNode.getId()].setSyncA(dtp.GetNewDateTime());
                    }
                    else
                    {
                        arrNodes[tNode.getId()].getVersionB().First = 0;
                        arrNodes[tNode.getId()].getVersionB().Second = -1;
                        dtp = new DateTimeParser(DateTime.Now);
                        arrNodes[tNode.getId()].setSyncB(dtp.GetNewDateTime());
                    }
                }
                else if (d.GetPairType().First == DirectionType.HDToSystem)
                {
                    if (systemName == SystemType.Host)
                    {
                        arrNodes[tNode.getId()].getVersionA().First = 0;
                        arrNodes[tNode.getId()].getVersionA().Second = -1;
                        dtp = new DateTimeParser(DateTime.Now);
                        arrNodes[tNode.getId()].setSyncA(dtp.GetNewDateTime());
                    }
                    else
                    {
                        arrNodes[tNode.getId()].getVersionB().First = -1;
                        arrNodes[tNode.getId()].getVersionB().Second = 0;
                        dtp = new DateTimeParser(DateTime.Now);
                        arrNodes[tNode.getId()].setSyncB(dtp.GetNewDateTime());
                    }
                }
            }
            #endregion
        }

        /// <summary>
        /// Does syncing based on the dfferent sync operations for folder
        /// </summary>
        /// <param name="d"></param>
        private void ReconcileFolder(Difference d)
        {
            TreeNode tNode;
            tNode = d.GetPartnerFolder();
            folderStack.Push(d);

            #region creation
            if (d.GetPairType().Second == OperationType.Creation)
            {
                if (d.GetPairType().First == DirectionType.SystemToHD)
                {
                    // To be updated for v2.0
                }
                else
                {
                    if (systemName == SystemType.Host)
                    {
                        if (tNode.getFullPathA().Length == 0)
                        {
                            TreeNode tParentNode;
                            tParentNode = arrNodes[tNode.getParent()];
                            Directory.CreateDirectory(Path.Combine(tParentNode.getFullPathA(), tNode.getName()));
                            arrNodes[tNode.getId()].setFullPathA(Path.Combine(tParentNode.getFullPathA(), tNode.getName()));
                            tNode.setFullPathA(Path.Combine(tParentNode.getFullPathA(), tNode.getName()));
                        }
                        else
                        {
                            Directory.CreateDirectory(tNode.getFullPathA());
                        }
                        arrNodes[tNode.getId()].getVersionA().First = tNode.getVersionB().First;
                        arrNodes[tNode.getId()].getVersionA().Second = tNode.getVersionB().Second;
                    }
                    else
                    {
                        Directory.CreateDirectory(tNode.getFullPathB());
                        arrNodes[tNode.getId()].getVersionB().First = tNode.getVersionA().First;
                        arrNodes[tNode.getId()].getVersionB().Second = tNode.getVersionA().Second;
                    }
                }

            }
            #endregion

            #region Modification
            else if (d.GetPairType().Second == OperationType.Modification)
            {
                if (d.GetPairType().First == DirectionType.SystemToHD)
                {
                    if (systemName == SystemType.Host)
                    {
                        arrNodes[tNode.getId()].getVersionA().First++;
                    }
                    else
                    {
                        arrNodes[tNode.getId()].getVersionB().Second++;
                    }
                }
                else
                {
                    if (systemName == SystemType.Host)
                    {
                        arrNodes[tNode.getId()].getVersionA().First = tNode.getVersionB().First;
                        arrNodes[tNode.getId()].getVersionA().Second = tNode.getVersionB().Second;
                    }
                    else
                    {
                        arrNodes[tNode.getId()].getVersionB().First = tNode.getVersionA().First;
                        arrNodes[tNode.getId()].getVersionB().Second = tNode.getVersionA().Second;
                    }
                }
            }
            #endregion

            #region Deletion
            else if (d.GetPairType().Second == OperationType.Deletion)
            {
                if (d.GetPairType().First == DirectionType.SystemToHD)
                {
                    if (systemName == SystemType.Host)
                    {
                        arrNodes[tNode.getId()].getVersionA().First = -1;
                        arrNodes[tNode.getId()].getVersionA().Second = 0;
                    }
                    else
                    {
                        arrNodes[tNode.getId()].getVersionB().First = 0;
                        arrNodes[tNode.getId()].getVersionB().Second = -1;
                    }
                }
                else
                {
                    if (systemName == SystemType.Host)
                    {
                        arrNodes[tNode.getId()].getVersionA().First = tNode.getVersionB().First;
                        arrNodes[tNode.getId()].getVersionA().Second = tNode.getVersionB().Second;
                    }
                    else
                    {
                        arrNodes[tNode.getId()].getVersionB().First = tNode.getVersionA().First;
                        arrNodes[tNode.getId()].getVersionB().Second = tNode.getVersionA().Second;
                    }
                }
            }
            #endregion

            #region Modification Modification Conflict
            else if (d.GetPairType().Second == OperationType.ModModConflict) // same as Modification...how?
            {
                if (d.GetPairType().First == DirectionType.SystemToHD)
                {
                    if (systemName == SystemType.Host)
                    {
                        arrNodes[tNode.getId()].getVersionA().First++;
                    }
                    else
                    {
                        arrNodes[tNode.getId()].getVersionB().Second++;
                    }
                }
                else
                {
                    if (systemName == SystemType.Host)
                    {
                        arrNodes[tNode.getId()].getVersionA().First = tNode.getVersionB().First;
                        arrNodes[tNode.getId()].getVersionA().Second = tNode.getVersionB().Second;
                    }
                    else
                    {
                        arrNodes[tNode.getId()].getVersionB().First = tNode.getVersionA().First;
                        arrNodes[tNode.getId()].getVersionB().Second = tNode.getVersionA().Second;
                    }
                }
            }
            #endregion

            #region Modification Deletion Conflict
            else if (d.GetPairType().Second == OperationType.ModDeleteConflict)
            {
                if (d.GetPairType().First == DirectionType.SystemToHD)
                {
                    if (systemName == SystemType.Host)
                    {
                        if (arrNodes[tNode.getId()].getVersionB().First == 0 && arrNodes[tNode.getId()].getVersionB().Second == -1)
                        {
                            arrNodes[tNode.getId()].getVersionA().First++;
                        }
                        else
                        {
                            arrNodes[tNode.getId()].getVersionA().First = -1;
                            arrNodes[tNode.getId()].getVersionA().Second = 0;
                        }

                    }
                    else
                    {
                        if (arrNodes[tNode.getId()].getVersionA().First == -1 && arrNodes[tNode.getId()].getVersionA().Second == 0)
                        {
                            arrNodes[tNode.getId()].getVersionB().Second++;
                        }
                        else
                        {
                            arrNodes[tNode.getId()].getVersionB().First = 0;
                            arrNodes[tNode.getId()].getVersionB().Second = -1;
                        }
                    }
                }
                else
                {
                    if (systemName == SystemType.Host)
                    {
                        if (arrNodes[tNode.getId()].getVersionB().First == 0 && arrNodes[tNode.getId()].getVersionB().Second == -1)
                        {
                            //Change to delete operation so that folder can be deleted in reconcileFolders method
                            d.SetPairType(new Pair<DirectionType, OperationType>(d.GetPairType().First, OperationType.Deletion));
                            arrNodes[tNode.getId()].getVersionA().First = tNode.getVersionB().First;
                            arrNodes[tNode.getId()].getVersionA().Second = tNode.getVersionB().Second;
                        }
                        else
                        {
                            arrNodes[tNode.getId()].getVersionA().First = tNode.getVersionB().First;
                            arrNodes[tNode.getId()].getVersionA().Second = tNode.getVersionB().Second;
                        }

                    }
                    else
                    {
                        if (arrNodes[tNode.getId()].getVersionA().First == -1 && arrNodes[tNode.getId()].getVersionA().Second == 0)
                        {
                            //Change to delete operation so that folder can be deleted in reconcileFolders method
                            d.SetPairType(new Pair<DirectionType, OperationType>(d.GetPairType().First, OperationType.Deletion));
                            arrNodes[tNode.getId()].getVersionB().First = tNode.getVersionA().First;
                            arrNodes[tNode.getId()].getVersionB().Second = tNode.getVersionA().Second;
                        }
                        else
                        {
                            arrNodes[tNode.getId()].getVersionB().First = tNode.getVersionA().First;
                            arrNodes[tNode.getId()].getVersionB().Second = tNode.getVersionA().Second;
                        }
                    }
                }
            }
#endregion

            #region Creation Creation Conflict
            else if (d.GetPairType().Second == OperationType.CreationCreationConflict)
            {
                if (d.GetPairType().First == DirectionType.SystemToHD)
                {
                    if (systemName == SystemType.Host)
                    {
                        arrNodes[tNode.getId()].getVersionA().First++;
                    }
                    else
                    {
                        arrNodes[tNode.getId()].getVersionB().Second++;
                    }
                }
                else
                {
                    if (systemName == SystemType.Host)
                    {
                        arrNodes[tNode.getId()].getVersionA().First = tNode.getVersionB().First;
                        arrNodes[tNode.getId()].getVersionA().Second = tNode.getVersionB().Second;
                    }
                    else
                    {
                        arrNodes[tNode.getId()].getVersionB().First = tNode.getVersionA().First;
                        arrNodes[tNode.getId()].getVersionB().Second = tNode.getVersionA().Second;
                    }
                }
            }
            #endregion

            #region Deletion Deletion Conflict
            else if (d.GetPairType().Second == OperationType.DeletionDeletionConflict)
            {
                if (d.GetPairType().First == DirectionType.SystemToHD)
                {
                    if (systemName == SystemType.Host)
                    {
                        arrNodes[tNode.getId()].getVersionA().First = -1;
                        arrNodes[tNode.getId()].getVersionA().Second = 0;
                    }
                    else
                    {
                        arrNodes[tNode.getId()].getVersionB().First = 0;
                        arrNodes[tNode.getId()].getVersionB().Second = -1;
                    }
                }
                else
                {
                    if (systemName == SystemType.Host)
                    {
                        arrNodes[tNode.getId()].getVersionA().First = 0;
                        arrNodes[tNode.getId()].getVersionA().Second = -1;
                    }
                    else
                    {
                        arrNodes[tNode.getId()].getVersionB().First = -1;
                        arrNodes[tNode.getId()].getVersionB().Second = 0;
                    }
                }
            }
            #endregion
        }

        /// <summary>
        /// Updates the folders time and size since they will be modified for any file operation
        /// </summary>
        private void UpdateFoldersTimeandSize()
        {
            TreeNode tNode;
            DateTimeParser dtp;
            DirectoryInfo dirInfo;

            foreach (Difference d in folderStack)
            {
                tNode = d.GetPartnerFolder();
                if (d.GetPairType().Second == OperationType.Deletion)
                {
                    dtp = new DateTimeParser(DateTime.Now);
                    if (d.GetPairType().First == DirectionType.SystemToHD)
                    {
                        if (systemName == SystemType.Host)
                        {
                            arrNodes[tNode.getId()].setSizeA(0);
                            arrNodes[tNode.getId()].setSyncA(dtp.GetNewDateTime());
                        }
                        else
                        {
                            arrNodes[tNode.getId()].setSizeB(0);
                            arrNodes[tNode.getId()].setSyncB(dtp.GetNewDateTime());
                        }
                    }
                    else
                    {
                        if (systemName == SystemType.Host)
                        {
                            // The directory's child are already deleted before executing this statement
                            Directory.Delete(tNode.getFullPathA());
                            arrNodes[tNode.getId()].setSizeA(0);
                            arrNodes[tNode.getId()].setSyncA(dtp.GetNewDateTime());
                        }
                        else
                        {
                            // The directory's child are already deleted before before executing this statement
                            Directory.Delete(tNode.getFullPathB());
                            arrNodes[tNode.getId()].setSizeB(0);
                            arrNodes[tNode.getId()].setSyncB(dtp.GetNewDateTime());
                        }
                    }
                }
                else // other folders that are not delete type can be updated as normal
                {
                    if (systemName == SystemType.Host)
                    {
                        dirInfo = new DirectoryInfo(tNode.getFullPathA());
                        long directorySize = Utility.CalculateDirectorySize(dirInfo);
                        dtp = new DateTimeParser(dirInfo.LastWriteTime);
                        arrNodes[tNode.getId()].setSizeA(directorySize);
                        arrNodes[tNode.getId()].setSyncA(dtp.GetNewDateTime());
                    }
                    else
                    {
                        dirInfo = new DirectoryInfo(tNode.getFullPathB());
                        long directorySize = Utility.CalculateDirectorySize(dirInfo);
                        dtp = new DateTimeParser(dirInfo.LastWriteTime);
                        arrNodes[tNode.getId()].setSizeB(directorySize);
                        arrNodes[tNode.getId()].setSyncB(dtp.GetNewDateTime());
                    }
                }
            }
        }

        /// <summary>
        /// For first time syncing. Copy files to Hard disk
        /// </summary>
        /// <param name="source">System A's path</param>
        public void CopyFilesToHD(DirectoryInfo source)
        {
            long fileSize = Utility.CalculateDirectorySize(source);
            bool hardDiskSpaceEnough = CheckDiskSpace(fileSize);

            if (!hardDiskSpaceEnough) throw new DiskSpaceNotEnoughException();

            DirectoryInfo destination = new DirectoryInfo(Path.Combine(storeFilesDir, "SecondTime"));
            Directory.CreateDirectory(destination.FullName);
            CopyDirectory(source, destination);
        }

        /// <summary>
        /// For second time syncing. Copy files to System B
        /// </summary>
        /// <param name="destination">System B's path</param>
        /// <returns></returns>
        public String CopyFilesToSystem(DirectoryInfo destination)
        {
            rootAddress = destination.FullName;

            if (Directory.Exists(rootAddress)) DeleteDirectory(new DirectoryInfo(rootAddress));

            DirectoryInfo source = new DirectoryInfo(Path.Combine(storeFilesDir, "SecondTime"));
            CopyDirectory(source, destination);
            updateArrNodesForSecondTime();
            DeleteDirectory(source);
            Directory.Delete(source.FullName, true);


            return rootAddress;
        }

        /// <summary>
        /// Update the meta data
        /// </summary>
        private void updateArrNodesForSecondTime()
        {
            DateTimeParser dtp;
            TreeNode tParentNode = arrNodes[0];
            int count = 0;
            DirectoryInfo d, rootB = new DirectoryInfo(rootAddress);
            FileInfo f;

            TreeOperation t = new TreeOperation(arrNodes, secondTime, systemName);
            t.UpdateRootNodeB(rootB, arrNodes[0]);

            foreach (TreeNode tNode in arrNodes)
            {
                tParentNode = arrNodes[tNode.getParent()];
                if (count != 0)
                {
                    tNode.setFullPathB(Path.Combine(tParentNode.getFullPathB(), tNode.getName()));
                }
                if (tNode.getType() == Type.File)
                {
                    f = new FileInfo(tNode.getFullPathB());
                    dtp = new DateTimeParser(f.LastWriteTime);
                    tNode.setSyncB(dtp.GetNewDateTime());
                }
                else
                {
                    d = new DirectoryInfo(tNode.getFullPathB());
                    dtp = new DateTimeParser(d.LastWriteTime);
                    tNode.setSyncB(dtp.GetNewDateTime());
                }
                tNode.setSizeB(tNode.getSizeA());
                // Version A might be modified before second time. So set version B to 1,0 then call analyze in Facade class
                tNode.getVersionB().First = 1;
                tNode.getVersionB().Second = 0;


                count++;
            }
        }

        /// <summary>
        /// Copy contents of the directory
        /// </summary>
        /// <param name="source"></param>
        /// <param name="destination"></param>
        private void CopyDirectory(DirectoryInfo source, DirectoryInfo destination)
        {
            try
            {

                if (!destination.Exists)
                {
                    try
                    {
                        destination.Create();
                    }
                    catch (UnauthorizedAccessException)
                    {
                    }
                }

                // Copy all files.
                FileInfo[] files = source.GetFiles();
                String filePath = "";
                FileAttributes fileAtts;
                foreach (FileInfo file in files)
                {
                    filePath = Path.Combine(destination.FullName, file.Name);
                    if (!File.Exists(filePath))
                    {
                        fileAtts = File.GetAttributes(file.FullName);
                        File.SetAttributes(file.FullName, fileAtts & ~FileAttributes.ReadOnly);
                        try
                        {
                            file.CopyTo(Path.Combine(@"\\?\UNC", filePath));
                        }
                        catch (UnauthorizedAccessException)
                        {
                        }
                    }
                }

                // Process subdirectories.
                DirectoryInfo[] dirs = source.GetDirectories();
                foreach (DirectoryInfo dir in dirs)
                {
                    string destinationDir = Path.Combine(destination.FullName, dir.Name);

                    // Call CopyDirectory() recursively.
                    CopyDirectory(dir, new DirectoryInfo(destinationDir));
                }
            }catch (PathTooLongException)
            {
                //Undo changes 
                //Currently it copies over as much as it can.
            }
        }

        /// <summary>
        /// Deletes the contents of the directory
        /// </summary>
        /// <param name="dirInfo"></param>
        public void DeleteDirectory(DirectoryInfo dirInfo)
        {
            // Copy all files.
            FileInfo[] files = dirInfo.GetFiles();
            FileAttributes fileAtts;
            DirectoryInfo dirDest;
            foreach (FileInfo file in files)
            {
                fileAtts = File.GetAttributes(file.FullName);
                File.SetAttributes(file.FullName, fileAtts & ~FileAttributes.ReadOnly);
                file.Delete();
            }

            // Process subdirectories.
            DirectoryInfo[] dirs = dirInfo.GetDirectories();
            foreach (DirectoryInfo dir in dirs)
            {
                string destinationDir = Path.Combine(dirInfo.FullName, dir.Name);
                dirDest = new DirectoryInfo(destinationDir);

                // Call CopyDirectory() recursively.
                DeleteDirectory(dirDest);
                dirDest.Delete();
            }
        }

        /// <summary>
        /// Hard disk stores file without the slashes and colon so that there will be a unique storing scheme
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        private String StripString(String input)
        {
            String[] arrWords;
            String concat = "";
            arrWords = input.Split('\\', ':');
            foreach (String str in arrWords)
            {
                concat += str;
            }
            return concat;
        }

        /// <summary>
        /// Method to see if hard disk has enough space
        /// </summary>
        /// <param name="fileSize"></param>
        /// <returns></returns>
        private bool CheckDiskSpace(long fileSize)
        {
            DriveInfo cDrive = new DriveInfo(storeFilesDir.Substring(0, 2)); // get the Drive. Eg: 'G:'
            long hardDiskSize = cDrive.AvailableFreeSpace;    //available space        
            if (fileSize > hardDiskSize) return false;
            else return true;
        }

        /// <summary>
        /// Check the size of files that will need to be synced to hard disk
        /// </summary>
        /// <returns></returns>
        private long ComputeFileSizeToTransfer()
        {
            long fileSize = 0;
            FileInfo f = null;
            foreach (Difference d in differences)
            {
                if (d.GetPairType().Second != OperationType.Deletion
                    && d.GetPairType().Second != OperationType.DeletionDeletionConflict
                    && d.GetPairType().Second != OperationType.ModDeleteConflict)
                {
                    if (d.GetItemType() == Type.File)
                    {
                        if (d.GetPartnerFile() != null && d.GetPairType().First == DirectionType.SystemToHD)
                        {
                            if (systemName == SystemType.Host)
                            {
                                if (d.GetPartnerFile().getFullPathA().Length != 0)
                                    f = new FileInfo(d.GetPartnerFile().getFullPathA());
                            }
                            else
                            {
                                if (d.GetPartnerFile().getFullPathB().Length != 0)
                                    f = new FileInfo(d.GetPartnerFile().getFullPathB()); // problem for firsttime
                            }
                            if (f != null)
                            {
                                fileSize += f.Length;
                                f = null; // set to null just to make sure next file is a fresh copy
                            }
                        }
                    }
                }
            }
            return fileSize;
        }

    }
}
